package de.tsystems.jsfdict.beans;

import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import de.tsystems.jsfdict.api.WordService;
import de.tsystems.jsfdict.model.Word;
import org.primefaces.event.RowEditEvent;
import org.primefaces.event.SelectEvent;
import org.primefaces.model.SelectableDataModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;

import javax.faces.application.FacesMessage;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.model.ListDataModel;
import javax.inject.Inject;
import javax.inject.Named;
import javax.validation.constraints.NotNull;
import java.io.IOException;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;

@Named("wordBean")
@Scope("session")
//@URLMapping(id = "words", pattern = "/words/", viewId = "/faces/default.xhtml")
@Controller
public class WordBean implements Serializable {
    private static final Logger LOG = LoggerFactory.getLogger(WordBean.class);

    @Inject
    @Named("wordService")
    private WordService wordService;
    private Word newWord = new Word();
    private WordDataModel wordDataModel = null;
    private Word selectedWord;

    @Inject
    @Named("translationBean")
    private TranslationBean translationBean;

    public TranslationBean getTranslationBean() {
        return translationBean;
    }

    public void setTranslationBean(TranslationBean translationBean) {
        this.translationBean = translationBean;
    }

    public void setWordService(WordService wordService) {
        this.wordService = wordService;
    }

    public Word getNewWord() {
        return newWord;
    }

    public void setNewWord(Word newWord) {
        this.newWord = newWord;
    }

    public void editWord(RowEditEvent event) {
        if (event.getObject() == null || !(event.getObject() instanceof Word)) {
            return;
        }
        LOG.debug("editWord called");
        Word eventObject = (Word) event.getObject();

        Word word = wordService.getById(eventObject.getId());

        word.setText(eventObject.getText());
        word.setLanguage(eventObject.getLanguage());

        wordService.save(word);
        wordDataModel.setWrappedData(getList());
    }

    public void createNew() {
        if (getList().contains(newWord)) {
            FacesMessage msg =
                    new FacesMessage(FacesMessage.SEVERITY_ERROR, "Duplicated", "This word has already been added");
            FacesContext.getCurrentInstance().addMessage(null, msg);
        } else {
            wordService.save(newWord);
            newWord = new Word();
        }
    }

    public List<Word> getList() {
        return wordService.listAll();
    }

    public void showTranslations(SelectEvent event) {
        FacesContext ctx = FacesContext.getCurrentInstance();

        Word word = (Word) event.getObject();

        ExternalContext extContext = ctx.getExternalContext();
        translationBean.setCurrentWord(word);
        String url = ctx.getApplication().getViewHandler().getActionURL(ctx, "/translations.xhtml");

        try {
            extContext.redirect(extContext.encodeActionURL(url));
        } catch (IOException e) {
            FacesMessage msg =
                    new FacesMessage(FacesMessage.SEVERITY_ERROR, e.getMessage(), e.getLocalizedMessage());
            FacesContext.getCurrentInstance().addMessage(null, msg);
            LOG.error("Error", e);
        }
    }

    public void setSelectedWord(Word selectedWord) {
        this.selectedWord = selectedWord;
    }

    public Word getSelectedWord() {
        return selectedWord;
    }

    public WordDataModel getWordDataModel() {
        if (wordDataModel == null) {
            wordDataModel = new WordDataModel(wordService);
        }
        wordDataModel.setWrappedData(getList());
        return wordDataModel;
    }

    public Collection<Word> getOtherWords(final Word word) {
        return Collections2.filter(getList(), new Predicate<Word>() {
            @Override
            public boolean apply(@NotNull Word input) {
                return !input.equals(word);
            }
        });
    }


    private static class WordDataModel extends ListDataModel<Word> implements SelectableDataModel<Word> {
        private final WordService wordService;

        public WordDataModel(WordService wordService) {
            this.wordService = wordService;
        }

        @Override
        public Object getRowKey(Word word) {
            return word.getId();
        }

        @Override
        public Word getRowData(String rowKey) {
            return wordService.getById(rowKey);
        }
    }
}
